package model;

import util.Validator;

/**
 * Represents a rational number.
 * 
 * @author Henrik Wall
 * @author Rudolf Schmidt
 * @author Eugen Nemirowski
 */
@SuppressWarnings("serial")
public class Rat extends Number {
	private final long numerator;
	private final long denominator;

	/**
	 * Constructor for {@link Rat}.
	 */
	private Rat(final long numerator, final long denominator) {
		this.numerator = numerator;
		this.denominator = denominator;
	}

	protected Rat(final long numerator) {
		this(numerator, 1);
	}

	/**
	 * @return a normalized {@link Rat}.
	 */
	public static Rat create(final long numerator, final long denominator) {
		return new Rat(numerator, Validator.nonZero(denominator, "denominator must not be zero")).normalize();
	}

	/**
	 * @return a normalized {@link Rat}.
	 */
	public static Rat create(final long numerator) {
		return create(numerator, 1);
	}

	/**
	 * Calculates the greatest common divisor of two integers.
	 * 
	 * @param a first integer
	 * @param b second integer
	 * @return the greatest common divisor of {@code a} and {@code b}
	 */
	public static long gcd(final long a, final long b) {
		return b == 0 ? a : gcd(b, a % b);
	}

	/**
	 * Creates a normalized version of {@code this}.
	 * A normalized {@link Rat} fulfills two conditions:
	 * <ol>
	 * <li>gcd(numerator, denominator) = 1</li>
	 * <li>this < 0 -> numerator < 0</li>
	 * </ol>
	 * @return
	 */
	private Rat normalize() {
		final long gcd = gcd(numerator, denominator);
		if (gcd == 1) {
			return this;
		}
		final long numerator = this.numerator / gcd;
		final long denominator = this.denominator / gcd;
		if (denominator < 0) {
			return new Rat(-numerator, -denominator);
		}
		return new Rat(numerator, denominator);
	}

	public long getNumerator() {
		return numerator;
	}

	public long getDenominator() {
		return denominator;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (denominator ^ (denominator >>> 32));
		result = prime * result + (int) (numerator ^ (numerator >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Rat))
			return false;
		final Rat other = (Rat) obj;
		return numerator == other.numerator && denominator == other.denominator;
	}

	@Override
	public String toString() {
		return "[" + numerator + "/" + denominator + ":" + getClass().getSimpleName() + "]";
	}

	@Override
	public int intValue() {
		return (int) longValue();
	}

	@Override
	public long longValue() {
		return numerator / denominator;
	}

	@Override
	public float floatValue() {
		return (float) doubleValue();
	}

	@Override
	public double doubleValue() {
		return numerator / (double) denominator;
	}
}